﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using Tierless.Framework.Data;
using Tierless.Framework.Entities;

namespace Tierless.Framework.Client
{
	public class TierlessEntity : INotifyPropertyChanged
	{
		public TierlessEntity(EntityRequests requests)
		{
			_worker = new WorkerQueue();
			_worker.AsyncOperationsComplete += new System.EventHandler(WorkerComplete);
			_worker.AsyncOperationsStarted += new System.EventHandler(WorkerStarted);
            if (requests != null)
            {
                Requests = requests;
                BeginLoad();
            }
        }

		public TierlessEntity() : this(null)
		{
		}

		private WorkerQueue _worker;

		private EntityRequests _requests;
		public EntityRequests Requests 
		{ 
			get { return _requests; }
			set
			{
				if (_requests != value)
				{
					_requests = value;
					NotifyPropertyChanged("Requests");
				}
			}
		}

		public Entity PrimaryEntity
		{
			get { return PrimarySet == null || PrimarySet.Count != 1 ? null : PrimarySet[0]; }
		}

		public EntitySet PrimarySet
		{
			get  { return _sets == null || _sets.Count == 0 ? null : _sets[0]; }
		}

		private ObservableCollection<EntitySet> _sets;

		public ObservableCollection<EntitySet> Sets
		{
			get { return _sets; }
		}

		private void InternalSetResults(IEnumerable<EntitySet> sets)
		{
			_sets = new ObservableCollection<EntitySet>(sets);
			NotifyPropertyChanged("Sets");
			NotifyPropertyChanged("PrimarySet");
			NotifyPropertyChanged("PrimaryEntity");
		}


		private bool _saveSuccessful;

		public bool SaveSuccessful
		{
			get { return _saveSuccessful; }
			private set
			{
				if (_saveSuccessful != value)
				{
					_saveSuccessful = value;
					NotifyPropertyChanged("SaveSuccessful");
				}
			}
		}

		protected void NotifyPropertyChanged(string propertyName)
		{
			if (PropertyChanged != null)
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
		}

		public event PropertyChangedEventHandler PropertyChanged;

		private void WorkerComplete(object sender, System.EventArgs e)
		{
			SetIsBusy(false);
		}

		private void WorkerStarted(object sender, System.EventArgs e)
		{
			SetIsBusy(true);
		}

		private bool _isBusy;

		public bool IsBusy
		{
			get { return _isBusy; }
			set
			{
				if (_isBusy != value)
				{
					_isBusy = value;
					NotifyPropertyChanged("IsBusy");
				}
			}
		}

		private void SetIsBusy(bool tempValue)
		{
			_worker.WaitOn(() => { IsBusy = tempValue; });
		}

		public void BeginLoad()
		{
			_worker.Queue
			(
				() =>
				{
					var client = new EntityClient();
					var result = client.Get(_requests);

					_worker.WaitOn(() => { InternalSetResults(result); });
				}
			);
		}

		/// <summary> Starts the save process. </summary>
		/// <remarks> WARNING: optimistic concurrency is not checked.  Any changes from the 
		/// time the saving starts to the time that results are obtained may be lost.  
		/// Prevent this by disallowing modification to the entities while the worker is busy. </remarks>
		public void BeginSave()
		{
			_worker.Queue
			(
				() =>
				{
					// Atypical pattern: to avoid taking an entire clone of the entity sets, the Begin... operation is treated as the copy (on the main thread)
					var client = new AsyncEntityClient();
					IAsyncResult saveHandle = null;
					_worker.WaitOn
					(
						() => 
						{ 
							saveHandle = client.BeginSave(_sets.ToList(), null, null); 
						}
					);
					saveHandle.AsyncWaitHandle.WaitOne();
					var result = client.EndSave(saveHandle);

					_worker.WaitOn
					(
						() => 
						{ 
							SaveSuccessful = result.Successful;
							if (result.EntitySets != null)
								InternalSetResults(result.EntitySets);
						}
					);
				}
			);
		}
	}
}
